
#ifdef _MSC_VER
#pragma once
#endif

#if USE_ITT_BUILD
/*
 * kmp_itt.h -- ITT Notify interface.
 * $Revision: 42829 $
 * $Date: 2013-11-21 05:44:01 -0600 (Thu, 21 Nov 2013) $
 */

/* <copyright>
    Copyright (c) 1997-2013 Intel Corporation.  All Rights Reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

      * Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
      * Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.
      * Neither the name of Intel Corporation nor the names of its
        contributors may be used to endorse or promote products derived
        from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

</copyright> */

#ifndef KMP_ITT_H
#define KMP_ITT_H

#include "kmp_lock.h"

#define INTEL_ITTNOTIFY_API_PRIVATE
#include "ittnotify.h"
#include "legacy/ittnotify.h"

#if KMP_DEBUG
    #define __kmp_inline __KMP_INLINE          // Turn off inlining in debug mode.
	#define _kmp_inline _KMP_INLINE
#else
    #define __kmp_inline __KMP_INLINE
	#define _kmp_inline _KMP_INLINE
#endif


#ifdef __cplusplus
EXTERN_C_BEGIN
#endif // __cplusplus

#if USE_ITT_NOTIFY
    KMP_GLOBAL kmp_int32  __kmp_itt_prepare_delay;

    KMP_GLOBALAPI void KMP_CDECL __kmp_itt_fini_ittlib(void);

#endif

// Simplify the handling of an argument that is only required when USE_ITT_BUILD is enabled.
#define USE_ITT_BUILD_ARG(x) ,x

KMP_GLOBALAPI void KMP_CDECL __kmp_itt_initialize();
KMP_GLOBALAPI void KMP_CDECL __kmp_itt_destroy();

// -------------------------------------------------------------------------------------------------
// New stuff for reporting high-level constructs.
// -------------------------------------------------------------------------------------------------

// Note the naming convention:
//     __kmp_itt_xxxing() function should be called before action, while
//     __kmp_itt_xxxed()  function should be called after action.

// --- Parallel region reporting ---
__kmp_inline void KMP_CDECL __kmp_itt_region_forking(  int gtid, int serialized = 0 ); // Master only, before forking threads.
__kmp_inline void KMP_CDECL __kmp_itt_region_joined(   int gtid, int serialized = 0 ); // Master only, after joining threads.
    // (*) Note: A thread may execute tasks after this point, though.

// --- Frame reporting ---
__kmp_inline void KMP_CDECL __kmp_itt_frame_submit( int gtid, __itt_timestamp begin, __itt_timestamp end, int imbalance, ident_t *loc );

// --- Barrier reporting ---
__kmp_inline void * KMP_CDECL __kmp_itt_barrier_object( int gtid, int bt, int set_name = 0, int delta = 0 );
__kmp_inline void   KMP_CDECL __kmp_itt_barrier_starting( int gtid, void * object );
__kmp_inline void   KMP_CDECL __kmp_itt_barrier_middle(   int gtid, void * object );
__kmp_inline void   KMP_CDECL __kmp_itt_barrier_finished( int gtid, void * object );

// --- Taskwait reporting ---
__kmp_inline void * KMP_CDECL __kmp_itt_taskwait_object( int gtid );
__kmp_inline void   KMP_CDECL __kmp_itt_taskwait_starting( int gtid, void * object );
__kmp_inline void   KMP_CDECL __kmp_itt_taskwait_finished(   int gtid, void * object );

// --- Task reporting ---
__kmp_inline void   KMP_CDECL __kmp_itt_task_starting( void * object );
__kmp_inline void   KMP_CDECL __kmp_itt_task_finished( void * object );

// --- Lock reporting ---
__kmp_inline void   KMP_CDECL __kmp_itt_lock_creating(  kmp_user_lock_p lock );
__kmp_inline void   KMP_CDECL __kmp_itt_lock_acquiring( kmp_user_lock_p lock );
__kmp_inline void   KMP_CDECL __kmp_itt_lock_acquired(  kmp_user_lock_p lock );
__kmp_inline void   KMP_CDECL __kmp_itt_lock_releasing( kmp_user_lock_p lock );
__kmp_inline void   KMP_CDECL __kmp_itt_lock_cancelled( kmp_user_lock_p lock );
__kmp_inline void   KMP_CDECL __kmp_itt_lock_destroyed( kmp_user_lock_p lock );

// --- Critical reporting ---
__kmp_inline void   KMP_CDECL __kmp_itt_critical_creating(  kmp_user_lock_p lock );
__kmp_inline void   KMP_CDECL __kmp_itt_critical_acquiring( kmp_user_lock_p lock );
__kmp_inline void   KMP_CDECL __kmp_itt_critical_acquired(  kmp_user_lock_p lock );
__kmp_inline void   KMP_CDECL __kmp_itt_critical_releasing( kmp_user_lock_p lock );
__kmp_inline void   KMP_CDECL __kmp_itt_critical_destroyed( kmp_user_lock_p lock );

// --- Single reporting ---
__kmp_inline void   KMP_CDECL __kmp_itt_single_start( int gtid );
__kmp_inline void   KMP_CDECL __kmp_itt_single_end(   int gtid );

// --- Ordered reporting ---
__kmp_inline void   KMP_CDECL __kmp_itt_ordered_init(  int gtid );
__kmp_inline void   KMP_CDECL __kmp_itt_ordered_prep(  int gtid );
__kmp_inline void   KMP_CDECL __kmp_itt_ordered_start( int gtid );
__kmp_inline void   KMP_CDECL __kmp_itt_ordered_end(   int gtid );

// --- Threads reporting ---
__kmp_inline void  KMP_CDECL __kmp_itt_thread_ignore();
__kmp_inline void  KMP_CDECL __kmp_itt_thread_name( int gtid );

// --- System objects ---
__kmp_inline void   KMP_CDECL __kmp_itt_system_object_created( void * object, const char * name );

// --- Stack stitching ---
__kmp_inline __itt_caller KMP_CDECL __kmp_itt_stack_caller_create(void);
__kmp_inline void KMP_CDECL __kmp_itt_stack_caller_destroy(__itt_caller);
__kmp_inline void KMP_CDECL __kmp_itt_stack_callee_enter(__itt_caller);
__kmp_inline void KMP_CDECL __kmp_itt_stack_callee_leave(__itt_caller);

// -------------------------------------------------------------------------------------------------
// Old stuff for reporting low-level internal synchronization.
// -------------------------------------------------------------------------------------------------

#if USE_ITT_NOTIFY

    /*
     * Support for SSC marks, which are used by SDE
     * http://software.intel.com/en-us/articles/intel-software-development-emulator
     * to mark points in instruction traces that represent spin-loops and are
     * therefore uninteresting when collecting traces for architecture simulation.
     */
    #ifndef INCLUDE_SSC_MARKS
    # define INCLUDE_SSC_MARKS (KMP_OS_LINUX && KMP_ARCH_X86_64)
    #endif

    /* Linux 64 only for now */
    #if (INCLUDE_SSC_MARKS && KMP_OS_LINUX && KMP_ARCH_X86_64)
    // Portable (at least for gcc and icc) code to insert the necessary instructions
    // to set %ebx and execute the unlikely no-op.
    # define INSERT_SSC_MARK(tag)                                           \
        __asm__ __volatile__ ("movl %0, %%ebx; .byte 0x64, 0x67, 0x90 " ::"i"(tag):"%ebx")
    #else
    # define INSERT_SSC_MARK(tag) ((void)0)
    #endif

    /* Markers for the start and end of regions that represent polling and
     * are therefore uninteresting to architectural simulations 0x4376 and
     * 0x4377 are arbitrary numbers that should be unique in the space of
     * SSC tags, but there is no central issuing authority rather
     * randomness is expected to work.
     */
    #define SSC_MARK_SPIN_START() INSERT_SSC_MARK(0x4376)
    #define SSC_MARK_SPIN_END()   INSERT_SSC_MARK(0x4377)

    // The object is an address that associates a specific set of the prepare, acquire, release,
    // and cancel operations.

    /* Sync prepare indicates a thread is going to start waiting for another thread
       to send a release event.  This operation should be done just before the thread
       begins checking for the existence of the release event */

    /* Sync cancel indicates a thread is cancelling a wait on another thread anc
       continuing execution without waiting for the other thread to release it */

    /* Sync acquired indicates a thread has received a release event from another
       thread and has stopped waiting.  This operation must occur only after the release
       event is received. */

    /* Sync release indicates a thread is going to send a release event to another thread
       so it will stop waiting and continue execution. This operation must just happen before
       the release event. */

    #define KMP_FSYNC_PREPARE(   obj )  __itt_fsync_prepare(   (void *)( obj ) )
    #define KMP_FSYNC_CANCEL(    obj )  __itt_fsync_cancel(    (void *)( obj ) )
    #define KMP_FSYNC_ACQUIRED(  obj )  __itt_fsync_acquired(  (void *)( obj ) )
    #define KMP_FSYNC_RELEASING( obj )  __itt_fsync_releasing( (void *)( obj ) )

    /*
        In case of waiting in a spin loop, ITT wants KMP_FSYNC_PREPARE() to be called with a delay
        (and not called at all if waiting time is small). So, in spin loops, do not use
        KMP_FSYNC_PREPARE(), but use KMP_FSYNC_SPIN_INIT() (before spin loop),
        KMP_FSYNC_SPIN_PREPARE() (whithin the spin loop), and KMP_FSYNC_SPIN_ACQUIRED().
        See KMP_WAIT_YIELD() for example.
    */

    #undef  KMP_FSYNC_SPIN_INIT
    #define KMP_FSYNC_SPIN_INIT( obj, spin )    \
        int sync_iters = 0;                     \
        if ( __itt_fsync_prepare_ptr ) {        \
            if ( obj == NULL ) {                \
                obj = spin;                     \
            } /* if */                          \
        } /* if */                              \
        SSC_MARK_SPIN_START()

    #undef  KMP_FSYNC_SPIN_PREPARE
    #define KMP_FSYNC_SPIN_PREPARE( obj ) do {                          \
        if ( __itt_fsync_prepare_ptr && sync_iters < __kmp_itt_prepare_delay ) { \
            ++ sync_iters;                                              \
            if ( sync_iters >= __kmp_itt_prepare_delay ) {              \
                KMP_FSYNC_PREPARE( (void*) obj );                       \
            } /* if */                                                  \
        } /* if */                                                      \
     } while (0)
    #undef  KMP_FSYNC_SPIN_ACQUIRED
    #define KMP_FSYNC_SPIN_ACQUIRED( obj ) do {         \
        SSC_MARK_SPIN_END();                            \
        if ( sync_iters >= __kmp_itt_prepare_delay ) {  \
            KMP_FSYNC_ACQUIRED( (void*) obj );          \
        } /* if */                                      \
     } while (0)

    /* ITT will not report objects created within KMP_ITT_IGNORE(), e. g.:
           KMP_ITT_IGNORE(
               ptr = malloc( size );
           );
    */
    #define KMP_ITT_IGNORE( statement ) do {                            \
            __itt_state_t __itt_state_;                                 \
            if ( __itt_state_get_ptr ) {                                \
                __itt_state_ = __itt_state_get();                       \
                __itt_obj_mode_set( __itt_obj_prop_ignore, __itt_obj_state_set ); \
            }  /* if */                                                 \
            { statement }                                               \
            if ( __itt_state_get_ptr ) {                                \
                __itt_state_set( __itt_state_ );                        \
            }  /* if */                                                 \
    } while (0)

    KMP_GLOBALCONST int KMP_MAX_FRAME_DOMAINS = 512; // Maximum number of frame domains to use (maps to
                                           // different OpenMP regions in the user source code).
    KMP_GLOBAL kmp_int32 __kmp_frame_domain_count;
    KMP_GLOBAL __itt_domain* __kmp_itt_domains[KMP_MAX_FRAME_DOMAINS];
#else

// Null definitions of the synchronization tracing functions.
# define KMP_FSYNC_PREPARE(   obj )        ((void)0)
# define KMP_FSYNC_CANCEL(    obj )        ((void)0)
# define KMP_FSYNC_ACQUIRED(  obj )        ((void)0)
# define KMP_FSYNC_RELEASING( obj )        ((void)0)

# define KMP_FSYNC_SPIN_INIT( obj, spin )  ((void)0)
# define KMP_FSYNC_SPIN_PREPARE(  obj )    ((void)0)
# define KMP_FSYNC_SPIN_ACQUIRED( obj )    ((void)0)

# define KMP_ITT_IGNORE(stmt ) do { stmt } while (0)

#endif // USE_ITT_NOTIFY

#ifdef __cplusplus
EXTERN_C_END
#endif // __cplusplus

#if /*! KMP_DEBUG ||*/ USE_ITT_NOTIFY
    // In release mode include definitions of inline functions.
    #include "kmp_itt.inl"
#endif

#endif // KMP_ITT_H

#else  /* USE_ITT_BUILD */

// Null definitions of the synchronization tracing functions.
// If USE_ITT_BULID is not enabled, USE_ITT_NOTIFY cannot be either.
// By defining these we avoid unpleasant ifdef tests in many places.
# define KMP_FSYNC_PREPARE(   obj )        ((void)0)
# define KMP_FSYNC_CANCEL(    obj )        ((void)0)
# define KMP_FSYNC_ACQUIRED(  obj )        ((void)0)
# define KMP_FSYNC_RELEASING( obj )        ((void)0)

# define KMP_FSYNC_SPIN_INIT( obj, spin )  ((void)0)
# define KMP_FSYNC_SPIN_PREPARE(  obj )    ((void)0)
# define KMP_FSYNC_SPIN_ACQUIRED( obj )    ((void)0)

# define KMP_ITT_IGNORE(stmt ) do { stmt } while (0)

# define USE_ITT_BUILD_ARG(x)

#endif /* USE_ITT_BUILD */
